/***************************************************************************
 *   Copyright (C) 2008 by Eduardo Gurgel Pinho                            *
 *   edgurgel@gmail.com                                                    *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 3 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
***************************************************************************/

#ifndef __RAYTRACER_H__
#define __RAYTRACER_H__

#include <boost/tuple/tuple.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/version.hpp>
#include "common/color.h"
#include "common/camera.h"
#include "common/scene.h"
#include "common/image.h"
#include "math/ray.h"
#include "math/vector.h"
#include "object/object.h"
#include "photonmapping/photon_map.h"

using namespace boost::tuples;

namespace Sirat {

namespace Ray_Tracing {

	/**
	 * The rendering process occurs here.
	 */

class Raytracer {
public:
	
	/**
	 * Simple constructor.
	 * No extra definitions as Indirect Illumination and Sample rays are needed.
	 * @param  _scene Collection of lights and objects to be rendered.
	 * @param  _camera
	 * @param  _depth Depth of reflections.
	 */
	Raytracer(Common::Scene* _scene,Common::Camera* _camera,const unsigned short _depth);
	
	/**
	 * Complete constructor
	 * @param _scene Collection of lights and objects to be rendered.
	 * @param _camera 
	 * @param _depth Depth of reflections.
	 * @param _indirect_illumination 
	 * @param _sample_rays Number of samples to indirect illumination calculation.
	 */
	Raytracer(Common::Scene* _scene,Common::Camera* _camera,const unsigned short _depth,bool _indirect_illumination,unsigned int _sample_rays);
	
	/**
	 * Default constructor.
	 * Boost Serialization needs a default constructor
	 * @see Raytracer(const Raytracer&)
	 */
	Raytracer(void);
	
	/**
	 * Copy constructor
	 * @param _raytracer 
	 * @see Raytracer()
	 */
	Raytracer(const Raytracer& _raytracer);
	
	/**
	 * Destructor.
	 */
	~Raytracer(void);

	
	/**
	 * Main function.
	 * Starts rendering process.
	 * @see stop_render()
	 */
	void render(void) const;
	/**
	 * Cancel render process.
	 * @see render()
	 */
	void stop_render(void);
	
	/**
	 * Change current scene.
	 * @param _scene 
	 */
	void set_scene(Common::Scene* _scene);
	
	/**
	 * Change current camera.
	 * @param _camera 
	 */
	void set_camera(Common::Camera* _camera);
	
	/**
	 * Change current output.
	 * @param _output 
	 */
	void set_output(Common::Image* _image);
	
	/**
	 * Change current photon map.
	 * @param _photon_map 
	 */
	void set_photon_map(Photon_Mapping::Photon_Map* _photon_map,const unsigned int _sample_photons);
	
	/**
	 * Change current depth of recursion on rendering.
	 * @param _depth 
	 */
	void set_depth(const unsigned short _depth);
	
	/**
	 * Enable/Disable direct illumination calculation.
	 * @param _direct_illumination 
	 */
	void set_direct_illumination(const bool _direct_illumination);
	
	/**
	 * Enable/Disable indirect illumination calculation.
	 * @param _indirect_illumination 
	 */
	void set_indirect_illumination(const bool _indirect_illumination);
	
	/**
	 * Set how many rays will be used calculate indirect illumination.
	 * @param _sample_rays 
	 */
	void set_sample_rays(const unsigned int _sample_rays);
	
	/**
	 * Enable/Disable soft shadow calculation.
	 * @param _soft_shadow
	 */
	void set_soft_shadow(const bool _soft_shadow);
	
	/**
	 * Set how many shadow rays will be used calculate soft shadow.
	 * @param _shadow_rays 
	 */
	void set_shadow_rays(const unsigned int _shadow_rays);
	
	#ifdef _OPENMP
	
	/**
	 * Enable/Disable parallel ray tracing.
	 * @param _parallel
	 */
	void set_parallel(const bool _parallel);
	
	/**
	 * Specify how many threads will be used.
	 * @param _threads
	 */
	void set_threads(const unsigned int _threads);
	
	#endif

	/**
	 * @return Current scene.
	 */
	Common::Scene* get_scene(void) const;
	
	/**
	 * @return Current camera.
	 */
	Common::Camera* get_camera(void) const;
	
	/**
	 * @return Current output.
	 */
	Common::Image* get_output(void) const;
	
	/**
	 * @return Current Photon Map.
	 */
	Photon_Mapping::Photon_Map* get_photon_map(void) const;
	
	/**
	 * @return Depth of reflections.
	 */
	unsigned short get_depth(void) const;
	
	bool get_direct_illumination(void) const;
	
	bool get_indirect_illumination(void) const;
	
	/**
	 * @return Number of sample rays.
	 */
	unsigned int get_sample_rays(void) const;
	
	bool get_soft_shadow(void) const;
	
	/**
	 * @return Number of rays that will be used on soft shadow calculation.
	 */
	unsigned int get_shadow_rays(void) const;
	
	/**
	 * @return Current rendering progress(0%-100%).
	 */
	double get_progress(void) const;
	
	#ifdef _OPENMP
	
	bool get_parallel(void) const;
	
	/**
	 * @return Number of threads that will be used on rendering.
	 */
	unsigned int get_threads(void) const;
	
	#endif	
	
	Raytracer& operator=(const Raytracer& _raytracer);
private:
	/**
	 * @param _ray 
	 * @return Nearest object and its position.
	 */
	tuple<Object::Object*,Math::Vector> trace(const Math::Ray& _ray) const;
	
	/**
	 * Color calculation recursive function.
	 * @param _object Nearest object intersected by @param _ray;
	 * @param _ray Inconming ray.
	 * @param _point Nearest intersection point from @param _object.
	 * @param _depth Current depth.
	 * @return Result color.
	 */
	Common::Color calculate_color_rec(Object::Object* _object,const Math::Ray& _ray,const Math::Vector& _point,const unsigned short _depth) const;
	
	friend class boost::serialization::access;
	/**
	 * Boost serialization needs serialize function to save and load.
	 * @param ar Archive (XML, Binary or Plain-Text)
	 * @param _version Current version file.
	 */
	template<class Archive> void serialize(Archive& ar, const unsigned int _version);

	
	Common::Scene* scene;
	Common::Camera* camera;
	Common::Image* image;
	Photon_Mapping::Photon_Map* photon_map;
	
	/**
	 * Configuration
	 * (Probably a Config class will be created)
	 */
	unsigned short depth;
	bool direct_illumination;
	bool indirect_illumination;
	unsigned int sample_rays;
	bool soft_shadow;
	unsigned int shadow_rays;
	unsigned int sample_photons;
	
	/**
	 * Progress information and stop rendering.
	 */
	mutable double progress;
	mutable volatile bool stop;
	
	#ifdef _OPENMP
	bool parallel;
	unsigned int threads;
	#endif
};

template<class Archive> void Raytracer::serialize(Archive& ar, const unsigned int /* file_version */){
	ar	& BOOST_SERIALIZATION_NVP(depth)
		& BOOST_SERIALIZATION_NVP(direct_illumination)
		& BOOST_SERIALIZATION_NVP(indirect_illumination)
		& BOOST_SERIALIZATION_NVP(sample_rays)
		& BOOST_SERIALIZATION_NVP(soft_shadow)
		& BOOST_SERIALIZATION_NVP(shadow_rays);
}

}

}

#endif
